#include "dev_key.h"
#define LOG_TAG        "[key]"
#include "log.h"



#define KEY_LOGIC_LEVEL     0
#if KEY_LOGIC_LEVEL == 0
#define KEY_READ_PIN(key)      (((GPIO_ISTAT(key->port) & (key->pin)) == 0) ? 0:1)
#else
#define KEY_READ_PIN(key)       (((GPIO_ISTAT(key->port) & (key->pin)) == 0) ? 1:0)
#endif

bs_key_pinmap_t keys_pin[KEY_NUM] = {
    [0] = {KEY_ID_0, GPIOC, GPIO_PIN_1},
    [1] = {KEY_ID_1, GPIOA, GPIO_PIN_0},
    [2] = {KEY_ID_2, GPIOE, GPIO_PIN_4},
};

key_manager_t  key_mgr;


/*!
    \brief      key event process
    \param[in]  none
    \param[out] none
    \retval     none
*/
__weak void bs_key_onclick_cb(bs_key_t *key)
{
    LOGI("key_%d single click", key->id);
}


/*!
    \brief      key event process
    \param[in]  none
    \param[out] none
    \retval     none
*/
__weak void bs_key_ondblclick_cb(bs_key_t *key)
{
    LOGI("key_%d double click", key->id);
}

/*!
    \brief      key event process
    \param[in]  none
    \param[out] none
    \retval     none
*/
__weak void bs_key_long_press_cb(bs_key_t *key)
{
    LOGI("key_%d long_press", key->id);
}


/*!
    \brief      direct key init
    \param[in]  none
    \param[out] none
    \retval     none
*/
void key_module_init()
{
    /*configure GPIO as input mode */
    for(int i=0; i<=KEY_NUM; i++)
    {
        if(keys_pin[i].port ==0 ) continue;
        gpio_mode_set(keys_pin[i].port, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, keys_pin[i].pin);
    }

    /* initial key application layer */
    for(int i = 0;i < KEY_NUM; i++)
    {
        key_mgr.keys[i].id = (bs_key_id_t)i;
        key_mgr.keys[i].state = KEY_IDLE;
        key_mgr.keys[i].last_press_time = 0;
        key_mgr.keys[i].stable_status = 0;
        key_mgr.keys[i].debounce_time = 0;
        key_mgr.keys[i].last_status = 0;
        key_mgr.keys[i].awaiting_dblclick = 0;
    }

    key_mgr.on_single_click = bs_key_onclick_cb;
    key_mgr.on_double_click = bs_key_ondblclick_cb;
    key_mgr.on_long_press = bs_key_long_press_cb;
    key_mgr.on_press = NULL;
    key_mgr.on_release = NULL;

    key_mgr.timer = 0;
    key_mgr.long_press_time = LONG_PRESS_TIME;
    key_mgr.double_click_time = DOUBLE_CLICK_TIME;
}

/*!
    \brief      scan key
    \param[in]  none
    \param[out] none
    \retval     none
*/
void bs_key_process()
{
    int             i;
    bs_key_t        *key;
    uint8_t         cur_status;
    key_manager_t   *manager = &key_mgr;
    bs_key_pinmap_t *key_pin;


    for(i = 0; i < KEY_NUM; i++)
    {
        // 按键处理程序可能阻塞，需要重新获取时间
        manager->timer = bs_jiffies;
    
        key = &manager->keys[i];
        key_pin = &keys_pin[i];
        cur_status = KEY_READ_PIN(key_pin);

        // 消抖
        if (key->stable_status != cur_status) {
            if (key->last_status != cur_status) {
                key->debounce_time = manager->timer + KEY_DEBOUNCE_TIME;
                key->last_status = cur_status;
                continue;
            } else {
                if (manager->timer < key->debounce_time) continue;
                key->stable_status = cur_status;
            }
        }

        // 判断按键状态
        switch(key->state)
        {
            case KEY_IDLE:
                if(cur_status) {
                    // 第一次按下
                    key->state = KEY_PRESSED;
                    key->last_press_time = manager->timer;
                    if(manager->on_press)manager->on_press(key);
                }
            break;

            case KEY_PRESSED:
                if(!cur_status) {
                    // 按键被释放了
                    key->state = KEY_RELEASED;
                    key->last_release_time = manager->timer;
                    if(manager->on_release)manager->on_release(key);

                    if(key->awaiting_dblclick) {
                        // 双击
                        key->awaiting_dblclick = 0;
                        key->state = KEY_DOUBLE_CLICK;
                        if(manager->on_double_click)manager->on_double_click(key);
                    }
                    else 
                        key->awaiting_dblclick = 1;
                }
                else if(manager->timer - key->last_press_time > manager->long_press_time) {
                    // 长按
                    key->state = KEY_LONG_PRESS;
                    if(manager->on_long_press)manager->on_long_press(key);
                }
            break;

            case KEY_RELEASED:
                if(cur_status){
                    // 按键又被按下了，有可能双击
                    key->state = KEY_PRESSED;
                    key->last_press_time = manager->timer;
                    if(manager->on_press)manager->on_press(key);
                }
                else if(manager->timer - key->last_release_time > manager->double_click_time) {
                    // 超过双击的检测时间，则认为单击
                    if(key->awaiting_dblclick) {
                        key->awaiting_dblclick = 0;
                        key->state = KEY_SINGLE_CLICK; 
                        if(manager->on_single_click)manager->on_single_click(key);         
                    }else 
                        key->state = KEY_IDLE;
                } else {
                    // nop
                }
            break;

            case KEY_SINGLE_CLICK:
            case KEY_DOUBLE_CLICK:
            // 长按有可能还没有释放
            case KEY_LONG_PRESS:
                if(!cur_status)
                {
                    key->state = KEY_IDLE;
                }
            break;

            default:
            break;
        }
    }
}
